package ast2

import (
	"fmt"
	"sync"

	"gitee.com/u-language/u-language/ucom/enum"
	"gitee.com/u-language/u-language/ucom/errcode"
	"gitee.com/u-language/u-language/ucom/internal/errutil"
	"gitee.com/u-language/u-language/ucom/lex2"
)

// found 是find的过去式
const opexpr_found_assign = "opexpr_found_assign"

const call_parser_fail = "call_parser_fail"

const op_bug = "op_bug"

const parserobj_nil = "parserobj_nil"

type stack struct {
	opStack       []enum.OPSymbol
	valueStack    []Expr
	opIndex       int
	valueIndex    int
	beforeisValue bool
	err           errcode.ErrCode
	inAutoFree    bool
	//是否在解析函数调用中
	inCall bool
}

const (
	equal        int8 = 0  //等于
	less_than    int8 = -1 //小于
	greater_than int8 = 1  //大于
)

// 操作的优先级枚举值，值越小，优先级越低
const (
	logicOr            = iota + 1 // ||
	logicAND                      // &&
	or                            // |
	xor                           // ^
	and                           // and
	judgeEqual                    // == !=
	compareSize                   // < >
	addAndSub                     // + -
	mulAndDlvAndRemain            // * / %
	paren                         // parentheses (
)

var stackpool = sync.Pool{
	New: func() interface{} {
		return &stack{
			valueIndex: -1,
			opIndex:    -1,
			valueStack: make([]Expr, 0, 2),
			opStack:    make([]enum.OPSymbol, 0, 1),
		}
	},
}

func newstack(inAutoFree bool, inCall bool) (s *stack) {
	s = stackpool.Get().(*stack)
	s.inAutoFree, s.inCall = inAutoFree, inCall
	return
}

// PushOp 将一个运算符放入运算符栈栈顶
func (s *stack) PushOp(op enum.OPSymbol, t *Tree) {
	if s.opIndex != -1 && !s.beforeisValue { //如果两个运算符相邻
		s.Panic(t, errcode.NewMsgUnexpected(opToStr(op)))
	} else {
		s.beforeisValue = false
	}
	s.opIndex++
	s.opStack = append(s.opStack[0:s.opIndex], op)
}

func objToStr(o *Object) string {
	if o.Kind == DerefObj {
		return "@"
	}
	if o.Kind == LeaObj {
		return "&"
	}
	return o.Name
}

// PushValue 将一个值放入值栈栈顶
func (s *stack) PushValue(v Expr, t *Tree) {
	if s.beforeisValue {
		if o, ok := v.(*Object); ok {
			s.Panic(t, errcode.NewMsgUnexpected(objToStr(o)))
		}
		//TODO:改进报错，对于选择器报告最左值，对于函数调用，报告函数名
		s.Panic(t, nil, errcode.OPbug)
	}
	s.beforeisValue = true
	s.valueIndex++
	s.valueStack = append(s.valueStack[0:s.valueIndex], v)
}

// PopOp 将一个运算符从运算符栈栈顶弹出
func (s *stack) PopOp() (op enum.OPSymbol) {
	op = s.opStack[s.opIndex]
	s.opIndex--
	return
}

// PopValue 将一个值从值栈栈顶弹出
func (s *stack) PopValue(tk lex2.Token, t *Tree) (n Expr) {
	if s.valueIndex <= -1 && tk != no {
		s.Panic(t, errcode.NewMsgUnexpected(t.lex2TokenToStr(tk)))
	}
	n = s.valueStack[s.valueIndex]
	s.valueIndex--
	return
}

// OpStackEmpty 判断运算符栈是否为空
func (s *stack) OpStackEmpty() bool {
	return s.opIndex == -1
}

// CmpOp 比较运算符与栈顶运算符的优先级
//
// 对于不能处理的，将panic
func (s *stack) CmpOp(op enum.OPSymbol) int8 {
	op2 := s.opStack[s.opIndex]
	if op2 == enum.LPARENOP || op2 == enum.CommaOP {
		return greater_than
	}
	openum := opPriorityEnum(op)
	op2enum := opPriorityEnum(op2)
	if openum > op2enum {
		return greater_than
	} else if openum < op2enum {
		return less_than
	}
	if openum == op2enum { //如果优先级相同，右边的比左边小
		return less_than
	}
	return 0
}

// opPriorityEnum 返回操作的优先级枚举值
func opPriorityEnum(op enum.OPSymbol) int8 {
	switch op {
	case enum.NoEqualOP, enum.EqualOP:
		return judgeEqual
	case enum.LessOP, enum.GreaterOP:
		return compareSize
	case enum.ADDOP, enum.SUBOP:
		return addAndSub
	case enum.MULOP, enum.DIVOP, enum.RemainOP:
		return mulAndDlvAndRemain
	case enum.AndOP:
		return and
	case enum.OrOP:
		return or
	case enum.XorOp:
		return xor
	case enum.LogicAndOP:
		return logicAND
	case enum.LogicOrOP:
		return logicOr
	case enum.LPARENOP:
		return paren
	default:
		panic(fmt.Errorf("不能处理的 op:%+v", op))
	}
}

var no = lex2.NewToken(lex2.No, "")

// AutoPopAndCmp 自动将运算符栈和值栈做出栈操作，直到运算符优先级大于运算符栈栈顶运算符优先级，或运算符栈为空
func (s *stack) AutoPopAndCmp(op enum.OPSymbol, t *Tree) {
	for !s.OpStackEmpty() && s.CmpOp(op) == less_than && len(s.valueStack) >= 2 {
		op := s.PopOp()           //弹出一个运算符
		src2 := s.PopValue(no, t) //弹出右边操作数
		src1 := s.PopValue(no, t) //弹出左边操作数
		s.beforeisValue = false
		s.PushValue(NewOpExpr(op, src1, src2, t.lex.Line), t) //将运算后的值压入值栈栈顶
	}
}

// AutoCmpAndPush 自动以合适的方式将运算符压入栈中
func (s *stack) AutoCmpAndPush(op enum.OPSymbol, t *Tree) {
	if s.OpStackEmpty() { //空栈
		s.PushOp(op, t) //直接压入栈中
	} else {
		switch s.CmpOp(op) {
		case less_than:
			s.AutoPopAndCmp(op, t)
			s.PushOp(op, t)
		case greater_than:
			s.PushOp(op, t)
		}
	}
}

func (s *stack) Panic(t *Tree, msg errcode.Msg, err ...errcode.ErrCode) {
	if s.err != errcode.NoErr {
		err_slice := make([]errcode.ErrCode, len(err)+1)
		err_slice[0] = s.err
		for i, v := range err {
			err_slice[i+1] = v
		}
		t.panic(msg, err_slice...)
	} else {
		t.panic(msg, err...)
	}
}

// AutoPush 自动将一个 [lex.Token] 推入值栈或运算符栈
func (s *stack) AutoPush(t *Tree, tk lex2.Token) (next lex2.Token) {
	switch tk.TYPE {
	case lex2.ADD: //是加号
		s.AutoCmpAndPush(enum.ADDOP, t)
	case lex2.SUB: //是减号
		s.AutoCmpAndPush(enum.SUBOP, t)
	case lex2.MUL: //是乘号
		s.AutoCmpAndPush(enum.MULOP, t)
	case lex2.DIV: //是除号
		s.AutoCmpAndPush(enum.DIVOP, t)
	case lex2.Equal: //比较是否相等
		s.AutoCmpAndPush(enum.EqualOP, t)
	case lex2.NoEqual: //比较是否不相等
		s.AutoCmpAndPush(enum.NoEqualOP, t)
	case lex2.Less: //比较是否小于
		s.AutoCmpAndPush(enum.LessOP, t)
	case lex2.Greater: //比较是否大于
		s.AutoCmpAndPush(enum.GreaterOP, t)
	case lex2.Remain: //是取余数
		s.AutoCmpAndPush(enum.RemainOP, t)
	case lex2.AND: //是位与运算
		s.AutoCmpAndPush(enum.AndOP, t)
	case lex2.Or: //是位或运算
		s.AutoCmpAndPush(enum.OrOP, t)
	case lex2.Xor: //是异或运算
		s.AutoCmpAndPush(enum.XorOp, t)
	case lex2.LogicAND: //是逻辑与运算
		s.AutoCmpAndPush(enum.LogicAndOP, t)
	case lex2.LogicOr: //是逻辑或运算
		s.AutoCmpAndPush(enum.LogicOrOP, t)
	case lex2.Int, lex2.NAME, lex2.FLOAT, lex2.String, lex2.TRUE, lex2.FALSE, lex2.LEA, lex2.Nil: //可能是值
		obj, next := t.parserObject(tk, s.err)
		if obj == nil {
			panic(parserobj_nil)
		}
		s.PushValue(obj, t)
		return next
	case lex2.ASSIGN: //有赋值时不可能产生运算表达式
		s.Panic(t, nil, errcode.AFIE)
		panic(opexpr_found_assign)
	case lex2.LPAREN: //如果是左小括号
		if s.beforeisValue { //如果值跟着(，推测为函数调用
			line := t.lex.Line
			call, next := t.parserCall2(s.PopValue(tk, t), t.lex.Next(), line)
			call.nosemicolon = true
			s.beforeisValue = false
			s.PushValue(call, t)
			return next
		}
		s.PushOp(enum.LPARENOP, t)
	case lex2.RPAREN: //如果是右小括号
		if s.inCall { //如果正在解析函数调用
			return
		}
		ok := false
		for !s.OpStackEmpty() && len(s.valueStack) >= 2 {
			op := s.PopOp() //弹出一个运算符
			if op == enum.LPARENOP {
				ok = true
				break
			}
			src2 := s.PopValue(no, t)                     //弹出右边操作数
			src1 := s.PopValue(no, t)                     //弹出左边操作数
			expr := NewOpExpr(op, src1, src2, t.lex.Line) //将运算后的值压入值栈栈顶
			expr.Inparen = true
			s.beforeisValue = false
			s.PushValue(expr, t)
		}
		if !ok {
			if len(s.valueStack) == 1 {
				o, ok := s.valueStack[0].(*OpExpr)
				if ok && o.Inparen && s.opIndex == 0 { //如果是如同(1+2)这样的情况
					s.PopOp() //将(出栈
					return
				}
			}
			t.unExpected(tk, s.err)
		}
	case lex2.Deref: //如果是解引用
		n, next := t.parserDeref(s.err, t.lex.Next())
		s.PushValue(n, t)
		return next
	case lex2.LBRACK: //如果是[
		line := t.lex.Line
		ret, next := t.parserIndexExprOrGenericInstantiation(s.PopValue(tk, t), t.lex.Next(), line)
		s.beforeisValue = false
		s.PushValue(ret, t)
		return next
	case lex2.PERIOD: //如果是选择器
		start := s.PopValue(tk, t)
		o, ok := start.(*Object)
		if !ok {
			s.Panic(t, nil, errcode.SelectorLeftAndRightValueMustSymbol)
		}
		ret, next := t.parserSelect2(*o, s.err)
		s.beforeisValue = false
		s.PushValue(ret, t)
		return next
	case lex2.NewLine, lex2.EOF, lex2.LBRACE, lex2.RBRACK:
		return tk
	default:
		s.Panic(t, errcode.NewMsgUnexpected(t.lex2TokenToStr(tk)))
	}
	return
}

func predefined_error(err interface{}) bool {
	if str, ok := err.(string); ok && (str == opexpr_found_assign || str == call_parser_fail || str == op_bug || str == parserobj_nil) { //出现预定义的错误
		return true
	}
	return false
}

// AutoPop 自动将当前的运算符和值转换为一个表达式
func (s *stack) AutoPop(t *Tree) (n Expr) {
	for !s.OpStackEmpty() && s.valueIndex >= 1 { //还有运算符及至少两个值
		op := s.PopOp()         //弹出一个运算符
		if op == enum.CommaOP { //如果出现意外的逗号
			s.Panic(t, nil, errcode.UnexpectedComma)
			panic(op_bug)
		}
		src2 := s.PopValue(no, t) //弹出右边操作数
		src1 := s.PopValue(no, t) //弹出左边操作数
		s.beforeisValue = false
		s.PushValue(NewOpExpr(op, src1, src2,t.lex.Line), t) //将运算后的值压入值栈栈顶
	}
	if s.valueIndex != 0 || !s.OpStackEmpty() { //不是只有结果一个或还有运算符但没有足够的操作数
		return nil
	}
	return s.PopValue(no, t) //弹出转换后的表达式
}

func isExprEnd(next lex2.Token, line *int, incall bool) bool {
	if next.TYPE == lex2.NewLine || next.TYPE == lex2.EOF {
		*line++
	}
	if incall && next.TYPE == lex2.RPAREN {
		return true
	}
	//对于函数调用，因为只解析其中一个参数，所以不止右小括号，逗号也是结束的标志
	//对于一般的表达式，新一行与文件结束是结束的标志，对于if和 else if语句的布尔表达式，左大括号是结束的标志,对于for中的语句，分号是结束的标志
	//对于case语句，冒号是结束的标志
	//对于数组长度表达式，右中括号是结束的标志
	return next.TYPE == lex2.NewLine || next.TYPE == lex2.EOF ||
		next.TYPE == lex2.LBRACE || next.TYPE == lex2.SEMICOLON ||
		next.TYPE == lex2.Comma ||
		next.TYPE == lex2.Colon || next.TYPE == lex2.RBRACK
}

// AutoExprNode 自动将一个表达式转换为表达式节点
func (s *stack) AutoExprNode(t *Tree, tk lex2.Token) (ret Expr, next lex2.Token) {
	defer func() {
		if err := recover(); err != nil {
			if predefined_error(err) { //出现预定义的错误
				ret = nil
				return
			}
			panic(err)
		}
	}()
	//TODO:研究是否需要line
	line := 0
	if s.err == errcode.ElseErr && isExprEnd(tk, &line, s.inCall) {
		//对于else if {应该直接返回
		return nil, tk
	}
	next = tk
	for {
		next = s.AutoPush(t, next)
		if next.TYPE == lex2.No {
			next = t.lex.Next()
		}
		if isExprEnd(next, &line, s.inCall) { //如果发现结束标志
			break
		}
	}
	node := s.AutoPop(t) //获取表达式
	if node == nil {     //没有成功转换为一个表达式
		if s.inCall {
			if !s.OpStackEmpty() {
				op := s.PopOp()
				if op == enum.LPARENOP {
					return
				}
				//如果在函数表达式不为空却不符合语法
				s.Panic(t, errcode.NewMsgUnexpected(opToStr(op)))
			}
			//Note:如果在函数调用中，没有运算符要么为空，要么有多个值相邻（如 a b c），但是相邻的多个值已经在push的值栈时报错了，所有这里什么都不用做
		} else {
			if s.OpStackEmpty() && s.valueIndex == -1 { //如果本该为表达式却为空
				s.Panic(t, errcode.NewMsgUnexpected(t.lex2TokenToStr(next)))
			}
			s.Panic(t, nil, errcode.OPbug)
		}
		return nil, next
	}
	return node, next
}

func opToStr(op enum.OPSymbol) string {
	switch op {
	case enum.ADDOP:
		return "+"
	case enum.SUBOP:
		return "-"
	case enum.MULOP:
		return "*"
	case enum.DIVOP:
		return "/"
	case enum.LessOP:
		return "<"
	case enum.GreaterOP:
		return ">"
	case enum.EqualOP:
		return "=="
	case enum.NoEqualOP:
		return "!="
	case enum.RemainOP: //remainder
		return "%"
	case enum.IncOP: //Increment
		return "++"
	case enum.DecOP: //Decrement
		return "--"
	case enum.AndOP:
		return "and"
	case enum.OrOP:
		return "|"
	case enum.XorOp:
		return "^"
	case enum.LogicAndOP:
		return "&&"
	case enum.LogicOrOP:
		return "||"
	case enum.LPARENOP:
		return "("
	case enum.DerefOP:
		return "@"
	case enum.CommaOP:
		return ","
	}
	panic(errutil.NewErr2(errutil.CompileErr, "意外的运算符枚举"))
}

func (s *stack) Close() {
	s.opIndex, s.valueIndex, s.opStack, s.err, s.beforeisValue = -1, -1, s.opStack[0:0], errcode.NoErr, false
	stackpool.Put(s)
}
